﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using iNet.Tasks;

namespace iNet.IO
{
	/// <summary>
	/// 資料流複製工作。
	/// </summary>
	public class StreamCopyTask : Task
	{
		#region 欄位
		int _BufferSize = 4096;
		long? _CopySize;
		Stream _DestinationStream;
		Stream _SourceStream;
		#endregion
		#region 建構子
		/// <summary>
		/// 初始化新的 StreamCopyTask 執行個體。
		/// </summary>
		public StreamCopyTask()
		{ }
		/// <summary>
		/// 初始化新的 StreamCopyTask 執行個體。
		/// </summary>
		/// <param name="srcStream">來源資料流。</param>
		/// <param name="destStream">目標資料流。</param>
		public StreamCopyTask(Stream srcStream, Stream destStream)
		{
			_SourceStream = srcStream;
			_DestinationStream = destStream;
		}
		#endregion
		#region BufferSize
		/// <summary>
		/// 取得或設定複製資料時所使用的緩衝區位元組大小，預設值為 4096。
		/// </summary>
		public int BufferSize
		{
			get
			{
				return _BufferSize;
			}
			set
			{
				//確認參數
				if (value <= 0)
					throw new ArgumentOutOfRangeException();

				//設定參數
				this.SetInitializingParameter<int>(ref _BufferSize, value);
			}
		}
		#endregion
		#region CopySize
		/// <summary>
		/// 取得或設定要複製的位元組數量，Null (Visual Basic 為 Nothing) 表示複製所有資料。
		/// </summary>
		public long? CopySize
		{
			get
			{
				return _CopySize;
			}
			set
			{
				//確認參數
				if (value != null && value.Value <= 0)
					throw new ArgumentOutOfRangeException();

				//設定參數
				this.SetInitializingParameter<long?>(ref _CopySize, value);
			}
		}
		#endregion
		#region DestinationStream
		/// <summary>
		/// 取得或設定目標資料流。
		/// </summary>
		public Stream DestinationStream
		{
			get
			{
				return _DestinationStream;
			}
			set
			{
				this.SetInitializingParameter<Stream>(ref _DestinationStream, value);
			}
		}
		#endregion
		#region Execute
		protected override bool Execute(TaskVersion version)
		{
			//取得參數
			int bufferSize;
			long? copySize;
			Stream srcStream, destStream;
			if (!this.ReadValue<int>(version, ref _BufferSize, out bufferSize) ||
				!this.ReadValue<long?>(version, ref _CopySize, out copySize) ||
				!this.ReadValue<Stream>(version, ref _SourceStream, out srcStream) ||
				!this.ReadValue<Stream>(version, ref _DestinationStream, out destStream))
				return true;

			//取得複製資料長度
			if (copySize == null)
			{
				try
				{
					if (srcStream.CanSeek)
						copySize = (srcStream.Length - srcStream.Position);
				}
				catch
				{ }
			}

			//修正緩衝區大小
			if (copySize != null && bufferSize > copySize.Value)
				bufferSize = (int)copySize.Value;

			//回報進度
			this.ReportProgress(version, (copySize != null ? (double?)0 : null));

			//複製資料
			try
			{
				long copiedBytes = 0;
				long? restBytes = copySize;
				byte[] buffer = new byte[bufferSize];
				int readCount = srcStream.Read(buffer, 0, bufferSize);
				while (readCount > 0)
				{
					//中斷確認
					if (this.IsCanceling)
						break;

					//寫入資料
					destStream.Write(buffer, 0, readCount);
					copiedBytes += readCount;
					if (restBytes != null)
						restBytes -= readCount;

					//中斷確認
					if (this.IsCanceling)
						break;

					//建立訊息及顯示進度
					if (copySize != null)
						this.ReportProgress(version, (copiedBytes * 100.0 / copySize.Value));

					//讀取下一筆資料
					if (restBytes != null && bufferSize > restBytes.Value)
						bufferSize = (int)restBytes.Value;
					readCount = srcStream.Read(buffer, 0, bufferSize);
				}
			}
			catch (IOException ex)
			{
				if (!this.IsCanceling)
					return false;
			}

			//完成
			return true;
		}
		#endregion
		#region SourceStream
		/// <summary>
		/// 取得或設定來源資料流。
		/// </summary>
		public Stream SourceStream
		{
			get
			{
				return _SourceStream;
			}
			set
			{
				this.SetInitializingParameter<Stream>(ref _SourceStream, value);
			}
		}
		#endregion
	}
}
